import java.awt.Image;
import java.awt.Toolkit;
import java.io.BufferedInputStream;
import java.io.IOException;

class MJPEGParser {
	private final byte[] JPEG_START = new byte[] { (byte) 0xFF, (byte) 0xD8 };
	private final byte[] JPEG_END = new byte[] { (byte) 0xFF, (byte) 0xD9 };
	private final int INITIAL_BUFFER_SIZE = 4096;
	BufferedInputStream in;
	byte[] boundary;
	byte[] segment;
	byte[] buf;
	JpegPanel display;
	int cur, len;
	boolean canceled = false;

	public boolean isCanceled() {
		return canceled;
	}

	public void setCanceled(boolean canceled) {
		this.canceled = canceled;
		if (canceled) {
			try {
				in.close();
				System.exit(0);
			} catch (IOException e) {
			}
		}
	}

	public MJPEGParser(BufferedInputStream in, JpegPanel display) {
		this.in = in;
		this.boundary = JPEG_END;
		buf = new byte[INITIAL_BUFFER_SIZE];
		cur = 0;
		len = INITIAL_BUFFER_SIZE;
		this.display = display;
	}

	public boolean parse() throws IOException {
		int b;
		while (in != null && (b = in.read()) != -1) {
			append(b);
			if (checkBoundary()) {
				processSegment();
				cur = 0;
				return true;
			}
		}
		return false ;
	}

	protected void processSegment() {
		Image source;
		boolean found = false;

		int i;
		for (i = 0; i < cur - JPEG_START.length; i++) {
			if (segmentsEqual(buf, i, JPEG_START, 0, JPEG_START.length)) {
				found = true;
				break;
			}
		}
		if (found) {
			int segLength = cur - i;
			segment = new byte[segLength];
			System.arraycopy(buf, i, segment, 0, segLength);
			source = Toolkit.getDefaultToolkit().createImage(segment, 0,
					segLength);
			display.setImage(source);
		}
	}

	public byte[] getSegment() {
		return segment;
	}

	protected boolean segmentsEqual(byte[] b1, int b1Start, byte[] b2,
			int b2Start, int len) {
		if (b1Start < 0 || b2Start < 0 || b1Start + len > b1.length
				|| b2Start + len > b2.length) {
			return false;
		} else {
			for (int i = 0; i < len; i++) {
				if (b1[b1Start + i] != b2[b2Start + i]) {
					return false;
				}
			}
			return true;
		}
	}

	protected boolean checkBoundary() {
		return segmentsEqual(buf, cur - boundary.length, boundary, 0,
				boundary.length);
	}

	public int getBufferSize() {
		return len;
	}

	protected void append(int i) {
		if (cur >= len) {
			byte[] newBuf = new byte[len * 2];
			System.arraycopy(buf, 0, newBuf, 0, len);
			buf = newBuf;
			len = len * 2;
		}
		buf[cur++] = (byte) i;
	}
}